package main

import (
	"bufio"
	"container/list"
	"database/sql/driver"
	"flag"
	"fmt"
	"github.com/fcbox/dbreplay/dbreplay-server/util"
	"github.com/google/gopacket/pcap"
	"io"
	"net/http"
	"os"
	"runtime"
	"strings"
	"sync"
	"time"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	_ "net/http/pprof"
)

func init()  {
	flag.Parse()
}


var (
	fileObj io.Writer
	device         string = ""
	snapshot_len   int32  = 65535
	promiscuous    bool   = false
	err            error
	timeout        time.Duration = 30 * time.Second
	handle         *pcap.Handle
	ip                            = flag.String("ip", "127.0.0.1", "the ip of need to catch.")
	port                          = flag.Int("port", 3306, "the port to need to catch.")
	user						  = flag.String("user","root","the database user.")
	password					  = flag.String("pass","xiaodaoliu","the database password.")
	db					 		  = flag.String("db","pay","the default database .")
	fileName					  = flag.String("file","info.log","the replay file .")
	speed						  = flag.Int("speed",1,"the speed of bench")
	exit chan int = make(chan int)
	lsts [100]*list.List
	readM *map[string] *sql.DB
	lock sync.Mutex
)

func main()  {
	runtime.GOMAXPROCS(10)
	for i:=0;i<100;i++{
		lsts[i]=list.New()
	}
	flag.Parse()
	fmt.Printf("the target ip is : %s,the port is : %d,user is : %s,pass is : %s,db is : %s,file is : %s\n", *ip, *port,*user,*password,*db,*fileName)
	fileNames := strings.Split((*fileName),",")
	var readMap map[string] *sql.DB = make(map[string] *sql.DB)
	var beginTime int64 = 0
	var endTime int64 = 0
	var lists = make([]*list.List, len(fileNames))
	for i,file := range fileNames{
		f,_ := os.Open(file)
		bfRd:= bufio.NewReader(f)
		readItem,lst,begin,end :=util.ReadMap(bfRd)
		for key,item := range *readItem{
			readMap[key] = item
		}
		if i == 0{
			beginTime = begin
			endTime = end
		}
		if begin < beginTime {
			beginTime = begin
		}
		if end > endTime {
			endTime = end
		}
		lists[i] = lst
		f.Close()
	}
	/*
	for key,_ := range readMap{
		dsn := fmt.Sprintf("%s:%s@%s(%s:%d)/%s",*user,*password,"tcp",*ip,*port,*db)
		//fmt.Println(dsn)
		DB,err := sql.Open("mysql",dsn)
		if err != nil{
			fmt.Printf("Open mysql failed,err:%v\n",err)
			continue
		}
		DB.SetConnMaxLifetime(1000*time.Minute)  //最大连接周期，超过时间的连接就close
		DB.SetMaxOpenConns(1) //设置最大连接数
		DB.SetMaxIdleConns(1) //设置闲置连接数
		readMap[key]=DB

	}
	 */
	readM = &readMap
	//lst := (*readMap)["10.204.8.201:61075"]

	if readMap == nil{
		return
	}
	fmt.Println("readmap length is:",len(readMap))
	fmt.Println("begintime is:",beginTime)
	fmt.Println("endtime is:",endTime)
	timeStamp :=time.Now().UnixNano() / 1e6 - beginTime
	fmt.Println("values is:",timeStamp)
	for i,lst := range lists{
		dispatchCommand(lst,i,len(lists))
	}

	for i:=0;i<100;i++{
		go doCommand(i,timeStamp,beginTime)
	}

	go func() {
		http.ListenAndServe("127.0.0.1:6060", nil)
	}()

	for i:=0;i<100;i++{
		<- exit
		fmt.Println("game over.")
	}
	fmt.Println("real over.")
	close(exit)
}

func dispatchCommand(lst *list.List,index,leg int){
	seg := 100/leg
	for{
		comm :=lst.Front()
		if comm == nil{
			break
		}
		commond :=comm.Value.(*util.Command)
		if strings.HasPrefix(commond.CommandStr,"LOGIN"){
			lst.Remove(comm)
			continue
		}
		if comm != nil{
			lst.Remove(comm)
			hashCode := util.StringHashcode(commond.Session)
			i := hashCode%seg + index*seg
			lsts[i].PushBack(commond)
		}
		lst.Remove(comm)
	}
}
func doCommand(i int,timeStamp int64,beginTime int64)  {
	defer func(){exit <- 1}()
	var lst *list.List
	lst = lsts[i]
	initTime := beginTime+timeStamp
	for{
		now := time.Now().UnixNano() / 1e6
		comm :=lst.Front()
		if comm == nil{
			break
		}
		if comm.Value == nil{
			break
		}
		commond :=comm.Value.(*util.Command)
		if (commond.TimeStamp+timeStamp-initTime)/int64(*speed)+initTime<(now-500){
			if strings.HasPrefix(commond.CommandStr,"LOGIN"){
				lst.Remove(comm)
				continue
			}
			lock.Lock()
			DB,_ := (*readM)[commond.Session]
			lock.Unlock()
			if DB == nil {
				dsn := fmt.Sprintf("%s:%s@%s(%s:%d)/%s",*user,*password,"tcp",*ip,*port,*db)
				//fmt.Println(dsn)
				DB,err = sql.Open("mysql",dsn)
				if err != nil{
					fmt.Printf("Open mysql failed,err:%v\n",err)
					continue
				}
				DB.SetConnMaxLifetime(1000*time.Minute)  //最大连接周期，超过时间的连接就close
				DB.SetMaxOpenConns(1) //设置最大连接数
				DB.SetMaxIdleConns(1) //设置闲置连接数
				lock.Lock()
				(*readM)[commond.Session] = DB
				lock.Unlock()
			}
			if strings.HasPrefix(commond.CommandStr,"CLOSE"){
				DB.Close()
				//delete((*readM),commond.Session)
				lst.Remove(comm)
				continue
			}
			be := time.Now().Unix()
			_,err :=DB.Exec(commond.CommandStr)
			en := time.Now().Unix()-be
			if en > 1{
				fmt.Println("execute time is:",en,time.Now(),commond.Session,commond.CommandStr,err)
			}
			if err != nil{
				if  err == driver.ErrBadConn || strings.Contains(err.Error(),"close"){
					//fmt.Println(err)
					DB.Close()
					DB = nil
					dsn := fmt.Sprintf("%s:%s@%s(%s:%d)/%s",*user,*password,"tcp",*ip,*port,*db)
					//fmt.Println(dsn)
					DBN,err := sql.Open("mysql",dsn)
					if err != nil{
						fmt.Printf("Open mysql failed,err:%v\n",err)
						continue
					}
					DBN.SetConnMaxLifetime(1000*time.Minute)  //最大连接周期，超过时间的连接就close
					DBN.SetMaxOpenConns(1) //设置最大连接数
					DBN.SetMaxIdleConns(1) //设置闲置连接数
					lock.Lock()
					(*readM)[commond.Session] = DBN
					lock.Unlock()
					continue
				}
				fmt.Println("catch error : ",en,time.Now(),commond.Session,commond.CommandStr,err.Error())
			}

			lst.Remove(comm)
		}else {
			time.Sleep(500*time.Millisecond)
			continue
		}
	}
}